package org.ciallo.simplemusicplayer.data.model;

import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.room.*;

import java.util.Objects;

/**
 * @author Jukomu
 * @Description: 代表一首歌曲的实体类/模型
 * 包含了歌曲的各种元数据信息，并实现了 Parcelable 接口以便在组件间传递。
 * @Package: org.ciallo.simplemusicplayer.model
 * @Project: SimpleMusicPlayer
 * @name: Song
 * @Date: 2025/5/27-01:33
 * @Filename: Song
 */
@Entity(tableName = "songs",
        indices = { // 为常用查询字段添加索引以提高性能
                @Index(value = "path", unique = true), // 歌曲路径通常是唯一的
                @Index(value = "artist_id_room"), // 为避免与原有artistId冲突，改名
                @Index(value = "album_id_room")   // 为避免与原有albumId冲突，改名
        })
public class Song implements Parcelable {

    /**
     * 歌曲的唯一ID，作为数据库主键。
     * 这个ID可以来自 MediaStore (MediaStore.Audio.Media._ID) 或应用内部生成。
     */
    @PrimaryKey
    @ColumnInfo(name = "id") // Room中的列名
    private long id; // 来自 MediaStore 或数据库的唯一ID
    @ColumnInfo(name = "title")
    private String title; // 歌曲标题
    @ColumnInfo(name = "artist_name")
    private String artistName; // 歌手名
    /**
     * 来自 MediaStore 的歌手ID，用于关联。
     * 在Room中，如果希望建立关系，可能需要更复杂的处理或单独的Artist实体。
     * 这里暂时只作为信息存储。为避免与Parcelable中的artistId混淆，添加后缀。
     */
    @ColumnInfo(name = "artist_id_room")
    private long artistIdRoom; // 歌手ID (来自 MediaStore 或数据库)
    @ColumnInfo(name = "album_name")
    private String albumName; // 专辑名
    /**
     * 来自 MediaStore 的专辑ID，用于关联。
     * 在Room中，如果希望建立关系，可能需要更复杂的处理或单独的Album实体。
     * 这里暂时只作为信息存储。为避免与Parcelable中的albumId混淆，添加后缀。
     */
    @ColumnInfo(name = "album_id_room")
    private long albumIdRoom; // 专辑ID (来自 MediaStore 或数据库)
    @ColumnInfo(name = "path")
    private String path; // 文件路径 (关键！)
    @ColumnInfo(name = "duration")
    private long duration; // 时长 (毫秒)
    @ColumnInfo(name = "album_art_uri")
    private String albumArtUri; // 专辑封面URI (可以是 content://, file://, http://)
    @ColumnInfo(name = "folder_path")
    private String folderPath; // 歌曲所在文件夹路径
    @ColumnInfo(name = "date_added")
    private long dateAdded; // 添加到媒体库的时间戳 (毫秒)
    @ColumnInfo(name = "track_number")
    private int trackNumber; // 专辑中的曲目号 (例如，CD1的第一首是 1001)
    @ColumnInfo(name = "year")
    private int year; // 发行年份
    @ColumnInfo(name = "size")
    private long size; // 文件大小 (bytes)
    @ColumnInfo(name = "mime_type")
    private String mimeType; // 例如 "audio/mpeg"
    @ColumnInfo(name = "title_pinyin")
    private String titlePinyin; // 歌曲标题拼音，用于排序 (可选，但建议保留)
    @ColumnInfo(name = "album_artist") // 新增字段
    private String albumArtist; // 从 MediaStore.Audio.AudioColumns.ALBUM_ARTIST 获取
    // 通常这些信息会动态生成或不直接持久化在歌曲表本身
    @Ignore
    private String artistPinyin; // 歌手名拼音 (可选)
    @Ignore
    private String albumPinyin; // 专辑名拼音 (可选)
    @Ignore
    private String genre; // 歌曲流派 (可选)
    @Ignore
    private String composer; // 作曲家 (可选)
    @Ignore
    private int discNumber; // CD号 (可选)
    // 这些字段与播放状态相关，通常不直接存储在歌曲实体表中，
    // 而是由播放服务管理或在专门的状态表中存储。
    @Ignore
    private float playbackSpeed = 1.0f; // 播放速度 (当前歌曲的特定设置，可选)
    @Ignore
    private long lastPlayedPosition = 0; // 上次播放到的位置 (可选)
    @Ignore
    private boolean isFavorite = false; // 是否收藏 (可选，状态可以在专门的收藏表管理)
    @Ignore
    private String lyricsPath; // 本地歌词文件路径 (可选)
    @Ignore
    private String onlineLyricsUrl; // 在线歌词URL (可选)

    /**
     * Room 需要一个无参构造函数。
     * 如果其他构造函数存在，这个也必须存在。
     */
    public Song() {
    }

    /**
     * 简化的构造函数，用于快速创建Song对象，主要包含播放所需的核心信息。
     * 其他属性将使用默认值或在之后设置。
     * 这个构造函数主要用于非Room场景，例如从文件直接加载。
     * Room会使用setters或直接访问字段（如果public）。
     *
     * @param id          歌曲ID
     * @param title       歌曲标题
     * @param artistName  歌手名
     * @param albumName   专辑名
     * @param path        文件路径
     * @param duration    时长 (毫秒)
     * @param albumArtUri 专辑封面URI
     */
    @Ignore
    public Song(long id, String title, String artistName, String albumName, String path, long duration, String albumArtUri) {
        this.id = id;
        this.title = title;
        this.artistName = artistName;
        this.albumName = albumName;
        this.path = path;
        this.duration = duration;
        this.albumArtUri = albumArtUri;
        // 初始化其他字段为默认值
        this.artistIdRoom = -1L; // 使用L后缀表示long型默认值
        this.albumIdRoom = -1L;
        this.folderPath = "";
        this.dateAdded = 0L;
        this.trackNumber = 0;
        this.year = 0;
        this.size = 0L;
        this.mimeType = "";
        this.titlePinyin = "";
        this.artistPinyin = "";
        this.albumPinyin = "";
        this.genre = "";
        this.composer = "";
        this.discNumber = 0;
        this.playbackSpeed = 1.0f;
        this.lastPlayedPosition = 0L;
        this.isFavorite = false;
        this.lyricsPath = null;
        this.onlineLyricsUrl = null;
        this.albumArtist = null;
    }

    public Song(long id, String title, String artistName, long artistIdRoom, String albumName, long albumIdRoom, String path, long duration, String albumArtUri, String folderPath, long dateAdded, int trackNumber, int year, long size, String mimeType, String titlePinyin, String albumArtist, String artistPinyin, String albumPinyin, String genre, String composer, int discNumber, float playbackSpeed, long lastPlayedPosition, boolean isFavorite, String lyricsPath, String onlineLyricsUrl) {
        this.id = id;
        this.title = title;
        this.artistName = artistName;
        this.artistIdRoom = artistIdRoom;
        this.albumName = albumName;
        this.albumIdRoom = albumIdRoom;
        this.path = path;
        this.duration = duration;
        this.albumArtUri = albumArtUri;
        this.folderPath = folderPath;
        this.dateAdded = dateAdded;
        this.trackNumber = trackNumber;
        this.year = year;
        this.size = size;
        this.mimeType = mimeType;
        this.titlePinyin = titlePinyin;
        this.albumArtist = albumArtist;
        this.artistPinyin = artistPinyin;
        this.albumPinyin = albumPinyin;
        this.genre = genre;
        this.composer = composer;
        this.discNumber = discNumber;
        this.playbackSpeed = playbackSpeed;
        this.lastPlayedPosition = lastPlayedPosition;
        this.isFavorite = isFavorite;
        this.lyricsPath = lyricsPath;
        this.onlineLyricsUrl = onlineLyricsUrl;
    }

    // 全属性构造函数，可以保留，并用 @Ignore 标记，或者确保它与Room字段兼容
    // Room 主要会使用无参构造函数和setter（如果字段是private），或者直接访问public字段
    // --- Parcelable Implementation ---

    /**
     * Parcelable构造函数，用于从Parcel对象中反序列化Song对象。
     *
     * @param in 包含序列化数据的Parcel对象。
     */
    protected Song(Parcel in) {
        id = in.readLong();
        title = in.readString();
        artistName = in.readString();
        artistIdRoom = in.readLong();
        albumName = in.readString();
        albumIdRoom = in.readLong();
        path = in.readString();
        duration = in.readLong();
        albumArtUri = in.readString();
        folderPath = in.readString();
        dateAdded = in.readLong();
        trackNumber = in.readInt();
        year = in.readInt();
        size = in.readLong();
        mimeType = in.readString();
        titlePinyin = in.readString();
        albumArtist = in.readString();
        // 注意：被 @Ignore 标记的字段通常不应该包含在 Parcelable 中
    }

    /**
     * Parcelable CREATOR字段，用于从Parcel中创建Song对象实例或数组。
     */
    public static final Creator<Song> CREATOR = new Creator<Song>() {
        @Override
        public Song createFromParcel(Parcel in) {
            return new Song(in);
        }

        @Override
        public Song[] newArray(int size) {
            return new Song[size];
        }
    };

    /**
     * 描述Parcelable对象的内容种类。
     *
     * @return 通常返回0，除非是文件描述符等特殊情况。
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 将Song对象的字段写入到Parcel对象中。
     *
     * @param dest  目标Parcel对象。
     * @param flags 附加标志，关于对象如何被写入。
     */
    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeLong(id);
        dest.writeString(title);
        dest.writeString(artistName);
        dest.writeLong(artistIdRoom);
        dest.writeString(albumName);
        dest.writeLong(albumIdRoom);
        dest.writeString(path);
        dest.writeLong(duration);
        dest.writeString(albumArtUri);
        dest.writeString(folderPath);
        dest.writeLong(dateAdded);
        dest.writeInt(trackNumber);
        dest.writeInt(year);
        dest.writeLong(size);
        dest.writeString(mimeType);
        dest.writeString(titlePinyin);
        dest.writeString(albumArtist);
        // @Ignore 字段不写入 Parcel
    }

    // --- equals, hashCode, toString ---

    /**
     * 比较两个Song对象是否相等。
     * 优先比较ID（如果有效），其次比较文件路径（如果有效），最后比较核心元数据。
     *
     * @param o 要比较的对象。
     * @return 如果对象相等则返回true，否则返回false。
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Song song = (Song) o;
        // 优先比较 ID，如果ID有效且不为0或-1
        if (id != 0 && id != -1 && song.id != 0 && song.id != -1) {
            return id == song.id;
        }
        // 如果ID不可靠，则比较路径（假设路径是唯一的）
        if (path != null && song.path != null) {
            return path.equals(song.path);
        }
        // 最后回退到比较多个字段（不推荐作为主要比较方式）
        return duration == song.duration &&
                Objects.equals(title, song.title) &&
                Objects.equals(artistName, song.artistName) &&
                Objects.equals(albumName, song.albumName);
    }

    /**
     * 生成Song对象的哈希码。
     * 优先使用ID（如果有效），其次使用文件路径。
     *
     * @return 对象的哈希码。
     */
    @Override
    public int hashCode() {
        // 优先使用ID
        if (id != 0 && id != -1) {
            return Objects.hash(id);
        }
        // 其次使用路径
        if (path != null) {
            return Objects.hash(path);
        }
        return Objects.hash(title, artistName, albumName, duration);
    }

    @NonNull
    @Override
    public String toString() {
        return "Song{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", artistName='" + artistName + '\'' +
                ", path='" + path + '\'' +
                '}';
    }

    /*---- Getter----Setter -----*/

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getArtistName() {
        return artistName;
    }

    public void setArtistName(String artistName) {
        this.artistName = artistName;
    }

    public long getArtistIdRoom() {
        return artistIdRoom;
    }

    public void setArtistIdRoom(long artistIdRoom) {
        this.artistIdRoom = artistIdRoom;
    }

    public String getAlbumName() {
        return albumName;
    }

    public void setAlbumName(String albumName) {
        this.albumName = albumName;
    }

    public long getAlbumIdRoom() {
        return albumIdRoom;
    }

    public void setAlbumIdRoom(long albumIdRoom) {
        this.albumIdRoom = albumIdRoom;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public long getDuration() {
        return duration;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }

    public String getAlbumArtUri() {
        return albumArtUri;
    }

    public void setAlbumArtUri(String albumArtUri) {
        this.albumArtUri = albumArtUri;
    }

    public String getFolderPath() {
        return folderPath;
    }

    public void setFolderPath(String folderPath) {
        this.folderPath = folderPath;
    }

    public long getDateAdded() {
        return dateAdded;
    }

    public void setDateAdded(long dateAdded) {
        this.dateAdded = dateAdded;
    }

    public int getTrackNumber() {
        return trackNumber;
    }

    public void setTrackNumber(int trackNumber) {
        this.trackNumber = trackNumber;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public long getSize() {
        return size;
    }

    public void setSize(long size) {
        this.size = size;
    }

    public String getMimeType() {
        return mimeType;
    }

    public void setMimeType(String mimeType) {
        this.mimeType = mimeType;
    }

    public String getTitlePinyin() {
        return titlePinyin;
    }

    public void setTitlePinyin(String titlePinyin) {
        this.titlePinyin = titlePinyin;
    }

    public String getArtistPinyin() {
        return artistPinyin;
    }

    public void setArtistPinyin(String artistPinyin) {
        this.artistPinyin = artistPinyin;
    }

    public String getAlbumPinyin() {
        return albumPinyin;
    }

    public void setAlbumPinyin(String albumPinyin) {
        this.albumPinyin = albumPinyin;
    }

    public String getGenre() {
        return genre;
    }

    public void setGenre(String genre) {
        this.genre = genre;
    }

    public String getComposer() {
        return composer;
    }

    public void setComposer(String composer) {
        this.composer = composer;
    }

    public int getDiscNumber() {
        return discNumber;
    }

    public void setDiscNumber(int discNumber) {
        this.discNumber = discNumber;
    }

    public float getPlaybackSpeed() {
        return playbackSpeed;
    }

    public void setPlaybackSpeed(float playbackSpeed) {
        this.playbackSpeed = playbackSpeed;
    }

    public long getLastPlayedPosition() {
        return lastPlayedPosition;
    }

    public void setLastPlayedPosition(long lastPlayedPosition) {
        this.lastPlayedPosition = lastPlayedPosition;
    }

    public boolean isFavorite() {
        return isFavorite;
    }

    public void setFavorite(boolean favorite) {
        isFavorite = favorite;
    }

    public String getLyricsPath() {
        return lyricsPath;
    }

    public void setLyricsPath(String lyricsPath) {
        this.lyricsPath = lyricsPath;
    }

    public String getOnlineLyricsUrl() {
        return onlineLyricsUrl;
    }

    public void setOnlineLyricsUrl(String onlineLyricsUrl) {
        this.onlineLyricsUrl = onlineLyricsUrl;
    }

    public String getAlbumArtist() {
        return albumArtist;
    }

    public void setAlbumArtist(String albumArtist) {
        this.albumArtist = albumArtist;
    }
}
